
#ifdef SYPX_MEMCHECK
#include "mmgr.h"
#endif

#include "Actor.h"
#include "Actors.h"
#include "WolfInstance.h"
#include "DogInstance.h"
#include "SheepInstance.h"
#include "Level.h"
#include <algorithm>

namespace SheepDefence
{
	WolfInstance::WolfInstance()
	{
	}

	WolfInstance::~WolfInstance()
	{
	}

	WolfInstance::WolfInstance(SypX::FileInputStream& fin, Wolf* actor, Level* level)
	{
		SypX::ErrorLogger::getLogger() << "Loading WolfInstance...\n";

		fin >> x;
		fin >> y;
		fin >> angle;
		fin >> spawn;
		dest_x = x;
		dest_y = y;
		prev_x = x;
		prev_y = y;
		start_x = x;
		start_y = y;

		SypX::ErrorLogger::getLogger() << "Loaded: " << x << ", " << y << ", " << angle << ", " << spawn << "\n";

		type = WOLF;

		state = UNSPAWNED;
		stamState = NORMAL;
		pathState = OK;
		elapsed  = 0;
		target = NULL;
		this->level = level;
		this->actor = actor;
		hp = (float) actor->hp;
		stateAge = 0;

		sprite = new SypX::SpriteInstance();
		sprite->useSprite(actor->sprite);

		sprite->setPosition( SypX::Vec3(-999.9f, -999.9f, 0.3f) );
		sprite->setActiveAngle(angle);

		if (spawn == 0)
		{
			level->numWolves++;
			SypX::ErrorLogger::getLogger()<<"WOLF UP\n";
			sprite->setPosition(SypX::Vec2(start_x,start_y));
			//sprite->transform[3].x = start_x;
			//sprite->transform[3].y = start_y;
			changeState(IDLE);
		}
	}

	WolfInstance::WolfInstance(Wolf* actor, Level* level, float x, float y, int angle)
	{
		SypX::ErrorLogger::getLogger() << "Loading WolfInstance...\n";

		this->x = x;
		this->y = y;
		this->angle = angle;
		this->spawn = 0;
		dest_x = x;
		dest_y = y;
		prev_x = x;
		prev_y = y;
		start_x = x;
		start_y = y;

		SypX::ErrorLogger::getLogger() << "Loaded: " << x << ", " << y << ", " << angle << ", " << spawn << "\n";

		type = WOLF;

		state = UNSPAWNED;
		stamState = NORMAL;
		pathState = OK;
		elapsed  = 0;
		target = NULL;
		this->level = level;
		this->actor = actor;
		hp = (float) actor->hp;
		stateAge = 0;

		sprite = new SypX::SpriteInstance();
		sprite->useSprite(actor->sprite);

		sprite->setPosition( SypX::Vec3(-999.9f, -999.9f, 0.3f) );
		sprite->setActiveAngle(angle);

		if (spawn == 0)
		{
			level->numWolves++;
			SypX::ErrorLogger::getLogger()<<"WOLF UP\n";
			sprite->setPosition(SypX::Vec2(start_x,start_y));
			//sprite->transform[3].x = start_x;
			//sprite->transform[3].y = start_y;
			changeState(IDLE);
		}
	}

	void WolfInstance::boundaryCheck(Actor* actor)
	{
		float new_x = dest_x > 310 - actor->collision / 2 ? 310 - actor->collision / 2 : dest_x < actor->collision / 2 ? actor->collision / 2 : dest_x;
		float new_y = dest_y > 470 - actor->collision / 2 ? 470 - actor->collision / 2 : dest_y < actor->collision / 2 ? actor->collision / 2 : dest_y;

		if (x > 310 - actor->collision / 2 || x < actor->collision / 2 || y > 470 - actor->collision / 2 || y < actor->collision / 2)
		{
			if (new_x != dest_x || new_y != dest_y)
			{
				dest_x = new_x;
				dest_y = new_y;

				if (state == POUNCING)
				{
					changeState(RUNNING);
					target->changeState(IDLE);
					target->pursuer = NULL;
					target = NULL;		// Release target
				}
			}
		}
		else
		{
			if (state != POUNCING)
			{
				dest_x = new_x;
				dest_y = new_y;
			}
		}
	}

	void WolfInstance::act(float time)
	{
		stateAge += time;

		if (x > 310 || x < 10 || y > 470 || y < 10)
		{
			if (state == RUNNING)
			{
				boundaryCheck(actor);
				return;
			}
		}

		if (state == UNSPAWNED)
		{
			if (level->stateAge > spawn)
			{
				level->numWolves++;
				SypX::ErrorLogger::getLogger()<<"WOLF UP\n";
				sprite->setPosition(SypX::Vec2(start_x,start_y));
				changeState(IDLE);
			}
			else
				return;
		}

		if (state == DEAD || state == BITTEN)
		{
			return;
		}

		if (state == BITING)
		{
			target->hp -= actor->att * time;

			if (target->hp <= 0)
			{
				// use idle texture
				changeState(IDLE);
				level->numSheep--;
				SypX::ErrorLogger::getLogger()<<"SHEEP DOWN\n";
				target->changeState(DEAD);
				target = NULL;		// unload target
			}

			return;
		}

		if (state == POUNCING)
		{
			if (getDist(target, this) < 20)	// Target reached
			{
				// use biting texture
				changeState(BITING);
				target->changeState(BITTEN);
				return;
			}
			else
			{
				//dest_x = target->dest_x;
				//dest_y = target->dest_y;
				dest_x = target->x;
				dest_y = target->y;
			}
			return;
		}

		// RUNNING, FLEEING or IDLE

		if (level->dog->state != POUNCING && level->dog->state != BITING && getDist(level->dog, this) - (actor->fear + level->dog->actor->fear) * (actor->fear + level->dog->actor->fear) < 0)
		{
			dest_x = x + (x - level->dog->x) * 2;
			dest_y = y + (y - level->dog->y) * 2;

			// use flee texture, tail tucked
			changeState(FLEEING);
			return;
		}

		if (state == STUCK)
		{
			if ((dest_x == x && dest_y == y) || stateAge > 2)
			{
				// use idle texture
				dest_x = x;
				dest_y = y;
				changeState(IDLE);
			}
			else
			{
				SypX::Vec2 dest = level->environment->getDest(x, y, dest_x, dest_y, level->obstacles, actor->collision);
				dest_x = dest.x;
				dest_y = dest.y;
				//changeState(FLEEING);
				return;
			}
		}

		if (state == FLEEING)
		{
			if ((dest_x == x && dest_y == y) || stateAge > 2)
			{
				// use idle texture
				dest_x = x;
				dest_y = y;
				changeState(IDLE);
			}
			else
				return;
		}

		// HUNGRY, RUNNING or IDLE

		SypX::ErrorLogger::getLogger() << "10: " << level->timer_10 << "\n";

		if (level->timer_10 == 0 || state == IDLE)	// If RUNNING, only recalculate every sec
		{
			// Create list of sheep
			std::vector<SheepInstance*>::iterator i = level->sheep.begin();

			//Optimisations
			mySheep.clear();

			while (i != level->sheep.end())
			{
				std::pair<SheepInstance*, float> someSheep;

				someSheep.first = (*i);
				someSheep.second = getDist((*i), this);

				mySheep.push_back(someSheep);

				i++;
			}

			// Sort based on distance
			std::sort(mySheep.begin(), mySheep.end(), sheepComparator());

			std::vector<std::pair<SheepInstance*, float> >::iterator j = mySheep.begin();

			while (j != mySheep.end())
			{
				if (j->first->state != DEAD && j->first->state != BITTEN && j->first->state != TARGETED)
				{
					if ((state == HUNGRY && !collision(x, y, j->first->x, j->first->y, level->obstacles, actor->collision)) || (state != HUNGRY && !collision(x, y, j->first->x, j->first->y, level->obstacles, this, level->dog)))
					{
						dest_x = j->first->x;
						dest_y = j->first->y;

						if (j->second < actor->pounce * actor->pounce)
						{
							// use pouncing texture
							changeState(POUNCING);
							target = j->first;
							target->changeState(TARGETED);
							target->pursuer = this;
						}
						else if (state == IDLE)
							changeState(RUNNING);

						return;
					}
				}

				j++;
			}

			// No directly accessible sheep
			//SypX::Vec2 dest = level->environment->getDest(this, level->dog, level->sheep, level->obstacles);
			SypX::Vec2 dest = level->environment->getDest(this, level->dog, level->obstacles);

			dest_x = dest.x;
			dest_y = dest.y;

			if (dest_x == x && dest_y == y && state != HUNGRY)
				changeState(IDLE);
			else if (state != HUNGRY)
				changeState(RUNNING);

			return;
		}


		if (state == RUNNING || state == HUNGRY)
			return;

		if (state == IDLE)
		{
			if (stateAge > 2)
			{
				dest_x = x;
				dest_y = y;
			}

			float chance = 0.8f;
			chance = pow(chance, time);

			if (rand() % 10001 > chance * 10000)
			{
				dest_x = x + (rand() % 5 - 2) * actor->speed;
				dest_y = y + (rand() % 5 - 2) * actor->speed;

				// use walking/running texture
				changeState(RUNNING);
			}

			return;
		}

		dest_x = x;
		dest_y = y;

		changeState(IDLE);
	}

	void WolfInstance::respond()
	{
		if (state == DEAD)
		{
			// use dead texture
			dest_x = x;
			dest_y = y;
			const SypX::Vec4& p = sprite->getPositionRef();
			sprite->setPosition(SypX::Vec3(p.x, p.y, 0.02f));
			//sprite->transform[3].z = 0.02f;

			if (target != NULL)
			{
				target->changeState(IDLE);
				target->pursuer = NULL;
				target = NULL;		// Release target
			}

			return;
		}

		if (state == BITTEN)
		{
			// use bitten texture
			dest_x = x;
			dest_y = y;

			if (target != NULL)
			{
				target->changeState(IDLE);
				target->pursuer = NULL;
				target = NULL;		// Release target
			}

			return;
		}
	}

	bool WolfInstance::collide(Instance* target)
	{
		if (target == this || (target->type == SHEEP && state != FLEEING && state != RUNNING) || target->state == DEAD)
			return false;
		return true;
	}

	Actor* WolfInstance::getActor()
	{
		return actor;
	}
}
